import time
from datetime import datetime as dt
import shutil
from colorama import Fore, init
init(autoreset=True)
import os, sys, re, traceback
from natsort import natsorted 
from PyPDF2 import PdfReader, PdfWriter
import atexit
import multiprocessing

from hm.common import *
from hm.hos import *
from hm.serializer import Serializer
from report import HmDocx, to_pdf, logger, MYDIR

# 是否修复
FIX = True
if "--fix" not in sys.argv:
    FIX = False
FIX_success = f' {Fore.GREEN}(fixed)'  # 修复成功提示信息

# 标准notebook目录名称
REPORT_DIRS = ["0-Index", "1-Test Summary", "2-Test Executed", 
               "3-Objective Evidence", "4-Summary of Changes", "5-DTRs"]

# ODBC报告的通用父类
class NotebookDocx(HmDocx):
    def __init__(self, path):
        super().__init__(path)
        logger.info(f"*********** [初始化文件{self.filename}] **********{os.getpid()}")
        self.__init_dir()
        self.is_me = self._is_me()
    
    def _is_me(self):
        return True
        
    def __init_dir(self):
        '''
            初始化报告目录
        '''
        self.report_dirs = [{"key":"Index",  "attr":"index_dir"}, 
             {"key":"Test[ -_]?Summary",  "attr":"summary_dir"},
             {"key":"Test[ -_]?Execut", "attr":"execution_dir"}, 
             {"key":"Objective[ -_]?Evidence",  "attr":"evidence_dir"},
             {"key":"Summary[ -_]?of[ -_]?Changes", "attr":"summary_changes_dir"},
             {"key":"DTR", "attr":"DTR_dir"}
        ]

        self.dir = os.path.dirname(self.path) # 当前文档所在目录
        self.parent_dir = os.path.dirname(self.dir) # 当前文档所在目录的目录
        
        # 查找notebook目录
        self.notebook_dir = self.dir # Test Notebook 文件夹默认值(如果要检查的是总报告)
        for d in self.report_dirs:
            dir = find_file(d["key"], self.parent_dir, only_folder=True)
            if dir:
                self.notebook_dir = self.parent_dir
                break
        logger.info(f"notebook dir: {self.notebook_dir}")

        # 查找notebook下子文件夹路径
        for i,d in enumerate(self.report_dirs):
            dir = find_file(d["key"], self.notebook_dir, only_folder=True)
            setattr(self, d["attr"], dir)
            logger.info(f"{d['attr']}:{dir}")
            # 如果--fix, 目录不存在则创建目录
            if FIX:
                if not dir:
                    dir = os.path.join(self.notebook_dir, REPORT_DIRS[i])
                    os.makedirs(dir, exist_ok=True)
                    setattr(self, d["attr"], dir)

        logger.info(f"index dir: {self.index_dir}")
        logger.info(f"summary dir: {self.summary_dir}")
        logger.info(f"execution dir: {self.execution_dir}")
        logger.info(f"evidence dir: {self.evidence_dir}")
        logger.info(f"summary of change dir: {self.summary_changes_dir}")
        logger.info(f"DTR dir: {self.DTR_dir}")
        
        logger.info(f"Index path: {self.Index_path}")
        logger.info(f"summary of changes path: {self.summary_changes_path}")
        logger.info(f"DTR path: {self.DTR_path}")
        logger.info(f"final_report_path: {self.final_report_path}")
        
    @property
    def summary_changes_path(self):
        return find_file("Summary[ -_]?Of[ _-]?Change.+?docx", self.summary_changes_dir, only_file=True)
    
    @property
    def DTR_path(self):
        return find_file(r"Defect[ -_]?Tracking.+docx", self.DTR_dir, only_file=True)

    @property
    def Index_path(self):
        return find_file("Index.+?docx", self.index_dir, only_file=True)
    
    @property
    def final_report_path(self):
        return find_file("Test.?Report.+?docx", self.notebook_dir, only_file=True)
    
    def detect_chinese(self):
        '''
            检查是否有中文
        '''
        chinese_str = ' '.join(set(re.findall(r"([\u4e00-\u9fa5]+)", self._doc_string)))
        if chinese_str:
            yield f"{Fore.LIGHTYELLOW_EX}文档《{self.filename}》包含中文“{chinese_str}”"
    
    def check_empty_cell(self):
        for c in self._empty_cell:
            yield f"{self.filename}第{c[0]+1}个表格 第{c[1]+1}行 第{c[2]+1}列为空单元格"
    
    def check(self):
        for err in self.detect_chinese():
            yield err
        # for err in self.check_empty_cell():
        #     yield err
        
    def save(self, overwrite=False):
        if FIX or overwrite:
            # 如果文件没有被修改，则不保存
            # 这里通过md5实现不了，因为经python-docx打开的文档，即使没有对文档内容做任何修改，该文档原始数据也会有所变化
            # old_md5 = calculate_md5(backup_file)
            # new_md5 = calculate_md5(self.path)
            # if old_md5 == new_md5: # 不保存新文件
            # self.new_doc_xml = self.doc.element.xml
            # if self.original_doc_xml==self.new_doc_xml: # 文件没改动，无需保存，保留原始文件
            #     return
            self.doc.save(self.path)
        # return to original file
        else:
            shutil.copy2(self.copy_path, self.path)


# 包装print方法，
# 使得调用printf，同时保存打印信息到txt文档
class OUTPUT:
    instance = None
    def __new__(cls):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
            cls.path = os.path.join(MYDIR, "output.txt")
            cls.f = open(cls.path, "w", encoding='utf-8')
        return cls.instance
            
    def __init__(self):
        pass

    def remove_colorama(self, data):
        data = re.sub(
            "|".join([ re.escape(i) for i in 
            [Fore.YELLOW,
            Fore.BLUE,
            Fore.GREEN,
            Fore.RED,
            Fore.MAGENTA,
            Fore.CYAN,
            Fore.RESET,
            Fore.LIGHTGREEN_EX,
            Fore.LIGHTCYAN_EX,
            Fore.LIGHTYELLOW_EX,
            Fore.LIGHTBLACK_EX]])
            , "", data)
        return data
    
    def remove_all_colorama(self):
        self.f.close()
        with open(self.path, "r", encoding='utf-8') as f:
            data = f.read()
            data = self.remove_colorama(data)
            self.f = open(self.path, "w", encoding='utf-8')
            self.f.write(data)


def printf(*args, **kwargs):
    '''
        bad code...
    '''
    print(*args, **kwargs)
    if multiprocessing.current_process().name=='MainProcess':
        output = OUTPUT()
        data = "".join(args)
        if 'end' not in kwargs:
            data+="\n"
        else:
            data+=kwargs['end']
        data = output.remove_colorama(data)
        output.f.write(data)
    

class CheckRet:
    instance = None
    def __new__(cls):
        if not cls.instance:
            cls.instance = super().__new__(cls)
        return cls.instance

    def __init__(self):
        '''
            包装打印信息：
                old_string = "this is a warnning info"
                new_string = self.warinning(old_string)
                self.show_level如果为True，new_string打印到终端会变成 “warnning this is a warnning info”，其中warnning会标记为黄色
                self.show_level如果为False则new_string还是和原来的old_string一样
                fix_wrapper装饰器作用：如果fix参数为True， new_string 后面加上“FIX_success”
        '''
        self.show_level = True

    def fix_wrapper(fun):
        def wrapper(self, *args, **kwargs):
            if self.show_level:
                string = fun(self, *args, **kwargs)
            else:
                string = args[0]
            if FIX:
                if (len(args)>1 and args[1]) or ('fix' in kwargs and kwargs['fix']):
                    string += FIX_success
            return string
        return wrapper

    @fix_wrapper
    def info(self, string, fix=False):
        return f"[info] {string}"
    
    @fix_wrapper
    def warnning(self, string, fix=False):
        return f"{Fore.LIGHTYELLOW_EX}[warnning]{Fore.RESET} {string}"

    @fix_wrapper
    def error(self, string, fix=False):
        return f"{Fore.RED}[error]{Fore.RESET} {string}"
CR = CheckRet()
    

# 正则表达式汇总
REGEX_DICT = {
        "ExecutionTable":{
            "Passed":[r"Pass(ed)?\.?$", re.I],
            "Failed":[r"Fail(ed)?\.?$", re.I],
            "Not executed":[r"Not execut(ed)?\.?$", re.I],
            
            "SoftwareVersion":[r": *?([\w\.]+)", re.I],
            "ADD":[ r"ADD.*?: *?(.+)", re.I],
            "TestTime":[r"Test [\w ]*?time", re.I],
            "TimeSpent":[r"Test[\w ]*?time.*?([0-9.]+?) *?(hour|hrs|hours)", re.I],
            "requirements":[r'((?:[\w]{3,6}-){2,3}\d{3,5})((?:.(?!(?:[\w]{3,6}-){2,3}\d{3,4}))+)', re.DOTALL], # 零宽负向先行断言
            
            "test_date": [r"\d{1,2}?[-/]+?\w+?[-/]+?\d{4}", 0], 
            "redmines":[r"redmine.*?(\d+)", re.I],
            "test case edit":[r"test case edit[^\w]+?\w.+?\n", re.I],
            "note_evidence":[r"Note.+?find.+?evidence.+?path[^\w]*?([\w][^\n]*)", re.I],
            "MD5":[r"MD5[^\w]*?([\w]+)", re.I],
            "equipment":['SB[0-9A-Z]+', 0],
        },
        'Execution':{
            "case_name":[r"[^\w]+?(.+?).doc.?", re.I]
        },
        "Summary":{
            "case_name":[r"[^\w]+?(.+?).doc.?", re.I]
        },
    }

def ReSearchWrapper(t):
    '''
        myre_search = ReSearchWrapper("Summary")
        match = myre_search("case_name", 'search text')
        ret = match.group(1)
        通过REGEX_DICT的键来进行正则匹配
        本来是为了统一管理所有的正则表达式，但好像弄得有点复杂......
    '''
    def ReSearch(key, text, find_all=False, find_iter=False):
        if not text:
            return None
        nonlocal t
        d = REGEX_DICT[t]
        if find_all:
            return re.findall(d[key][0], text, d[key][1])
        elif find_iter:
            return re.finditer(d[key][0], text, d[key][1])
        else:
            return re.search(d[key][0], text, d[key][1])
    return ReSearch



def save_sign_page(pdf_path, page=-1):
    '''
        保存报告签字页，默认为最后一页
    '''
    if ('-f') in sys.argv:
        save_dir = parse_argv('-f')
    else:
        save_dir = sys.argv[1]
    if os.path.isfile(save_dir):
        save_dir = os.path.dirname(save_dir)
    print(save_dir)
    save_dir = os.path.join(save_dir, 'sign_page')
    os.makedirs(save_dir, exist_ok=True)
    reader = PdfReader(pdf_path)
    writer = PdfWriter()
    writer.add_page(reader.pages[page])
    save_path = os.path.join(save_dir, OSname(pdf_path)+"-sign_page.pdf")
    print("签字页保存在", save_path)
    with open(save_path, 'wb') as f:
        writer.write(f)
        

